/*
 * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package org.loon.framework.android.game.core.graphics.geom;

/**
 * The <code>BasicStroke</code> class defines a basic set of rendering
 * attributes for the outlines of graphics primitives, which are rendered
 * with a {@link Graphics2D} object that has its Stroke attribute set to
 * this <code>BasicStroke</code>.
 * The rendering attributes defined by <code>BasicStroke</code> describe
 * the shape of the mark made by a pen drawn along the outline of a
 * {@link Shape} and the decorations applied at the ends and joins of
 * path segments of the <code>Shape</code>.
 * These rendering attributes include:
 * <dl compact>
 * <dt><i>width</i>
 * <dd>The pen width, measured perpendicularly to the pen trajectory.
 * <dt><i>end caps</i>
 * <dd>The decoration applied to the ends of unclosed subpaths and
 * dash segments.  Subpaths that start and end on the same point are
 * still considered unclosed if they do not have a CLOSE segment.
 * See {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}
 * for more information on the CLOSE segment.
 * The three different decorations are: {@link #CAP_BUTT},
 * {@link #CAP_ROUND}, and {@link #CAP_SQUARE}.
 * <dt><i>line joins</i>
 * <dd>The decoration applied at the intersection of two path segments
 * and at the intersection of the endpoints of a subpath that is closed
 * using {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}.
 * The three different decorations are: {@link #JOIN_BEVEL},
 * {@link #JOIN_MITER}, and {@link #JOIN_ROUND}.
 * <dt><i>miter limit</i>
 * <dd>The limit to trim a line join that has a JOIN_MITER decoration.
 * A line join is trimmed when the ratio of miter length to stroke
 * width is greater than the miterlimit value.  The miter length is
 * the diagonal length of the miter, which is the distance between
 * the inside corner and the outside corner of the intersection.
 * The smaller the angle formed by two line segments, the longer
 * the miter length and the sharper the angle of intersection.  The
 * default miterlimit value of 10.0f causes all angles less than
 * 11 degrees to be trimmed.  Trimming miters converts
 * the decoration of the line join to bevel.
 * <dt><i>dash attributes</i>
 * <dd>The definition of how to make a dash pattern by alternating
 * between opaque and transparent sections.
 * </dl>
 * All attributes that specify measurements and distances controlling
 * the shape of the returned outline are measured in the same
 * coordinate system as the original unstroked <code>Shape</code>
 * argument.  When a <code>Graphics2D</code> object uses a
 * <code>Stroke</code> object to redefine a path during the execution
 * of one of its <code>draw</code> methods, the geometry is supplied
 * in its original form before the <code>Graphics2D</code> transform
 * attribute is applied.  Therefore, attributes such as the pen width
 * are interpreted in the user space coordinate system of the
 * <code>Graphics2D</code> object and are subject to the scaling and
 * shearing effects of the user-space-to-device-space transform in that
 * particular <code>Graphics2D</code>.
 * For example, the width of a rendered shape's outline is determined
 * not only by the width attribute of this <code>BasicStroke</code>,
 * but also by the transform attribute of the
 * <code>Graphics2D</code> object.  Consider this code:
 * <blockquote><tt>
 *      // sets the Graphics2D object's Tranform attribute
 *      g2d.scale(10, 10);
 *      // sets the Graphics2D object's Stroke attribute
 *      g2d.setStroke(new BasicStroke(1.5f));
 * </tt></blockquote>
 * Assuming there are no other scaling transforms added to the
 * <code>Graphics2D</code> object, the resulting line
 * will be approximately 15 pixels wide.
 * As the example code demonstrates, a floating-point line
 * offers better precision, especially when large transforms are
 * used with a <code>Graphics2D</code> object.
 * When a line is diagonal, the exact width depends on how the
 * rendering pipeline chooses which pixels to fill as it traces the
 * theoretical widened outline.  The choice of which pixels to turn
 * on is affected by the antialiasing attribute because the
 * antialiasing rendering pipeline can choose to color
 * partially-covered pixels.
 * <p>
 * For more information on the user space coordinate system and the
 * rendering process, see the <code>Graphics2D</code> class comments.
 * @see Graphics2D
 * @author Jim Graham
 */
public class BasicStroke implements Stroke {

	/**
	 * Joins path segments by extending their outside edges until they meet.
	 */
	public final static int JOIN_MITER = 0;

	/**
	 * Joins path segments by rounding off the corner at a radius of half the
	 * line width.
	 */
	public final static int JOIN_ROUND = 1;

	/**
	 * Joins path segments by connecting the outer corners of their wide
	 * outlines with a straight segment.
	 */
	public final static int JOIN_BEVEL = 2;

	/**
	 * Ends unclosed subpaths and dash segments with no added decoration.
	 */
	public final static int CAP_BUTT = 0;

	/**
	 * Ends unclosed subpaths and dash segments with a round decoration that has
	 * a radius equal to half of the width of the pen.
	 */
	public final static int CAP_ROUND = 1;

	/**
	 * Ends unclosed subpaths and dash segments with a square projection that
	 * extends beyond the end of the segment to a distance equal to half of the
	 * line width.
	 */
	public final static int CAP_SQUARE = 2;

	float width;

	int join;
	int cap;
	float miterlimit;

	float dash[];
	float dash_phase;

	/**
	 * Constructs a new <code>BasicStroke</code> with the specified attributes.
	 * 
	 * @param width
	 *            the width of this <code>BasicStroke</code>. The width must be
	 *            greater than or equal to 0.0f. If width is set to 0.0f, the
	 *            stroke is rendered as the thinnest possible line for the
	 *            target device and the antialias hint setting.
	 * @param cap
	 *            the decoration of the ends of a <code>BasicStroke</code>
	 * @param join
	 *            the decoration applied where path segments meet
	 * @param miterlimit
	 *            the limit to trim the miter join. The miterlimit must be
	 *            greater than or equal to 1.0f.
	 * @param dash
	 *            the array representing the dashing pattern
	 * @param dash_phase
	 *            the offset to start the dashing pattern
	 * @throws IllegalArgumentException
	 *             if <code>width</code> is negative
	 * @throws IllegalArgumentException
	 *             if <code>cap</code> is not either CAP_BUTT, CAP_ROUND or
	 *             CAP_SQUARE
	 * @throws IllegalArgumentException
	 *             if <code>miterlimit</code> is less than 1 and
	 *             <code>join</code> is JOIN_MITER
	 * @throws IllegalArgumentException
	 *             if <code>join</code> is not either JOIN_ROUND, JOIN_BEVEL, or
	 *             JOIN_MITER
	 * @throws IllegalArgumentException
	 *             if <code>dash_phase</code> is negative and <code>dash</code>
	 *             is not <code>null</code>
	 * @throws IllegalArgumentException
	 *             if the length of <code>dash</code> is zero
	 * @throws IllegalArgumentException
	 *             if dash lengths are all zero.
	 */
	public BasicStroke(float width, int cap, int join, float miterlimit,
			float dash[], float dash_phase) {
		if (width < 0.0f) {
			throw new IllegalArgumentException("negative width");
		}
		if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
			throw new IllegalArgumentException("illegal end cap value");
		}
		if (join == JOIN_MITER) {
			if (miterlimit < 1.0f) {
				throw new IllegalArgumentException("miter limit < 1");
			}
		} else if (join != JOIN_ROUND && join != JOIN_BEVEL) {
			throw new IllegalArgumentException("illegal line join value");
		}
		if (dash != null) {
			if (dash_phase < 0.0f) {
				throw new IllegalArgumentException("negative dash phase");
			}
			boolean allzero = true;
			for (int i = 0; i < dash.length; i++) {
				float d = dash[i];
				if (d > 0.0) {
					allzero = false;
				} else if (d < 0.0) {
					throw new IllegalArgumentException("negative dash length");
				}
			}
			if (allzero) {
				throw new IllegalArgumentException("dash lengths all zero");
			}
		}
		this.width = width;
		this.cap = cap;
		this.join = join;
		this.miterlimit = miterlimit;
		if (dash != null) {
			this.dash = (float[]) dash.clone();
		}
		this.dash_phase = dash_phase;
	}

	/**
	 * Constructs a solid <code>BasicStroke</code> with the specified
	 * attributes.
	 * 
	 * @param width
	 *            the width of the <code>BasicStroke</code>
	 * @param cap
	 *            the decoration of the ends of a <code>BasicStroke</code>
	 * @param join
	 *            the decoration applied where path segments meet
	 * @param miterlimit
	 *            the limit to trim the miter join
	 * @throws IllegalArgumentException
	 *             if <code>width</code> is negative
	 * @throws IllegalArgumentException
	 *             if <code>cap</code> is not either CAP_BUTT, CAP_ROUND or
	 *             CAP_SQUARE
	 * @throws IllegalArgumentException
	 *             if <code>miterlimit</code> is less than 1 and
	 *             <code>join</code> is JOIN_MITER
	 * @throws IllegalArgumentException
	 *             if <code>join</code> is not either JOIN_ROUND, JOIN_BEVEL, or
	 *             JOIN_MITER
	 */
	public BasicStroke(float width, int cap, int join, float miterlimit) {
		this(width, cap, join, miterlimit, null, 0.0f);
	}

	/**
	 * Constructs a solid <code>BasicStroke</code> with the specified
	 * attributes. The <code>miterlimit</code> parameter is unnecessary in cases
	 * where the default is allowable or the line joins are not specified as
	 * JOIN_MITER.
	 * 
	 * @param width
	 *            the width of the <code>BasicStroke</code>
	 * @param cap
	 *            the decoration of the ends of a <code>BasicStroke</code>
	 * @param join
	 *            the decoration applied where path segments meet
	 * @throws IllegalArgumentException
	 *             if <code>width</code> is negative
	 * @throws IllegalArgumentException
	 *             if <code>cap</code> is not either CAP_BUTT, CAP_ROUND or
	 *             CAP_SQUARE
	 * @throws IllegalArgumentException
	 *             if <code>join</code> is not either JOIN_ROUND, JOIN_BEVEL, or
	 *             JOIN_MITER
	 */
	public BasicStroke(float width, int cap, int join) {
		this(width, cap, join, 10.0f, null, 0.0f);
	}

	/**
	 * Constructs a solid <code>BasicStroke</code> with the specified line width
	 * and with default values for the cap and join styles.
	 * 
	 * @param width
	 *            the width of the <code>BasicStroke</code>
	 * @throws IllegalArgumentException
	 *             if <code>width</code> is negative
	 */
	public BasicStroke(float width) {
		this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
	}

	/**
	 * Constructs a new <code>BasicStroke</code> with defaults for all
	 * attributes. The default attributes are a solid line of width 1.0,
	 * CAP_SQUARE, JOIN_MITER, a miter limit of 10.0.
	 */
	public BasicStroke() {
		this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
	}

	/**
	 * Returns a <code>Shape</code> whose interior defines the stroked outline
	 * of a specified <code>Shape</code>.
	 * 
	 * @param s
	 *            the <code>Shape</code> boundary be stroked
	 * @return the <code>Shape</code> of the stroked outline.
	 */
	public Shape createStrokedShape(Shape s) {
		/*
		 * sun.java2d.pipe.RenderingEngine re =
		 * sun.java2d.pipe.RenderingEngine.getInstance(); return
		 * re.createStrokedShape(s, width, cap, join, miterlimit, dash,
		 * dash_phase);
		 */
		return null;
	}

	/**
	 * Returns the line width. Line width is represented in user space, which is
	 * the default-coordinate space used by Java 2D. See the
	 * <code>Graphics2D</code> class comments for more information on the user
	 * space coordinate system.
	 * 
	 * @return the line width of this <code>BasicStroke</code>.
	 * @see Graphics2D
	 */
	public float getLineWidth() {
		return width;
	}

	/**
	 * Returns the end cap style.
	 * 
	 * @return the end cap style of this <code>BasicStroke</code> as one of the
	 *         static <code>int</code> values that define possible end cap
	 *         styles.
	 */
	public int getEndCap() {
		return cap;
	}

	/**
	 * Returns the line join style.
	 * 
	 * @return the line join style of the <code>BasicStroke</code> as one of the
	 *         static <code>int</code> values that define possible line join
	 *         styles.
	 */
	public int getLineJoin() {
		return join;
	}

	/**
	 * Returns the limit of miter joins.
	 * 
	 * @return the limit of miter joins of the <code>BasicStroke</code>.
	 */
	public float getMiterLimit() {
		return miterlimit;
	}

	/**
	 * Returns the array representing the lengths of the dash segments.
	 * Alternate entries in the array represent the user space lengths of the
	 * opaque and transparent segments of the dashes. As the pen moves along the
	 * outline of the <code>Shape</code> to be stroked, the user space distance
	 * that the pen travels is accumulated. The distance value is used to index
	 * into the dash array. The pen is opaque when its current cumulative
	 * distance maps to an even element of the dash array and transparent
	 * otherwise.
	 * 
	 * @return the dash array.
	 */
	public float[] getDashArray() {
		if (dash == null) {
			return null;
		}

		return (float[]) dash.clone();
	}

	/**
	 * Returns the current dash phase. The dash phase is a distance specified in
	 * user coordinates that represents an offset into the dashing pattern. In
	 * other words, the dash phase defines the point in the dashing pattern that
	 * will correspond to the beginning of the stroke.
	 * 
	 * @return the dash phase as a <code>float</code> value.
	 */
	public float getDashPhase() {
		return dash_phase;
	}

	/**
	 * Returns the hashcode for this stroke.
	 * 
	 * @return a hash code for this stroke.
	 */
	public int hashCode() {
		int hash = Float.floatToIntBits(width);
		hash = hash * 31 + join;
		hash = hash * 31 + cap;
		hash = hash * 31 + Float.floatToIntBits(miterlimit);
		if (dash != null) {
			hash = hash * 31 + Float.floatToIntBits(dash_phase);
			for (int i = 0; i < dash.length; i++) {
				hash = hash * 31 + Float.floatToIntBits(dash[i]);
			}
		}
		return hash;
	}

	/**
	 * Returns true if this BasicStroke represents the same stroking operation
	 * as the given argument.
	 */
	/**
	 * Tests if a specified object is equal to this <code>BasicStroke</code> by
	 * first testing if it is a <code>BasicStroke</code> and then comparing its
	 * width, join, cap, miter limit, dash, and dash phase attributes with those
	 * of this <code>BasicStroke</code>.
	 * 
	 * @param obj
	 *            the specified object to compare to this
	 *            <code>BasicStroke</code>
	 * @return <code>true</code> if the width, join, cap, miter limit, dash, and
	 *         dash phase are the same for both objects; <code>false</code>
	 *         otherwise.
	 */
	public boolean equals(Object obj) {
		if (!(obj instanceof BasicStroke)) {
			return false;
		}

		BasicStroke bs = (BasicStroke) obj;
		if (width != bs.width) {
			return false;
		}

		if (join != bs.join) {
			return false;
		}

		if (cap != bs.cap) {
			return false;
		}

		if (miterlimit != bs.miterlimit) {
			return false;
		}

		if (dash != null) {
			if (dash_phase != bs.dash_phase) {
				return false;
			}

			if (!java.util.Arrays.equals(dash, bs.dash)) {
				return false;
			}
		} else if (bs.dash != null) {
			return false;
		}

		return true;
	}
}
